#include <iostream>
#include <gio/gio.h>
#include <cstdio>
#include "ExampleGdbusGenerated.h"
#include <vector>
#include "scene.h"

void work(AisdkDemo *delegate,
          GDBusMethodInvocation *invocation, ...) {
    va_list argp; /* 定义保存函数参数的结构 */
    int argno = 0; /* 纪录参数个数 */
    GVariant *para; /* 存放取出的字符串参数 */

// 使用宏va_start, 使argp指向传入的第一个可选参数，
// 注意 msg是参数表中最后一个确定的参数，并非参数表中第一个参数
    va_start( argp, invocation );
    work(delegate, invocation, argp);

    para = va_arg( argp, GVariant *);
    g_print("handleRequest input: %s\n", para);

    para = va_arg( argp, GVariant *);
    g_print("handleRequest input: %s\n", para);

    va_end( argp ); /* 将argp置为NULL */

//    aisdk_demo_complete_method3(delegate, invocation, "haha");
}

typedef void (WorkProcess)(void *, GDBusMethodInvocation *, GVariant *);
typedef void (Hook)();
typedef struct callBack {
    std::string signalName;
    Hook *preHook;
    WorkProcess *process;
    Hook *postHook;
} CallBack;
std::vector<CallBack *> registerCallBack;

void process(void *delegate, GDBusMethodInvocation *invocation, GVariant *input) {
    gchar *str;
    g_variant_get(input, "(s)", &str);
    g_print("process input: %s\n", str);

    auto delegateTmp = (AisdkDemo *) delegate;
    aisdk_demo_complete_method2(delegateTmp, invocation, 1000, "haha");
}

bool handleRequest(void *delegate,
                   GDBusMethodInvocation *invocation,
                   GVariant *input, gpointer userData) {
//    gint input1, input2;
//    GVariant *receive = g_variant_get_variant(input);
//    g_variant_get(receive,"(ii)", &input1, &input2);
//
//    g_print("handleRequest input: %d %d\n", input1, input2);
//
//    aisdk_demo_complete_method1(delegate, invocation, 100);

//    const gchar *typeString = g_variant_get_type_string(input);
//    g_print("handler_request type string: %s\n", typeString);

    auto * work = (WorkProcess *)userData;

    work(delegate, invocation, input);

    return true;
}

void connectSignal(void *delegate) {
    g_signal_connect(delegate, "handle-method1",
                     G_CALLBACK(handleRequest), nullptr);
    g_signal_connect(delegate, "handle-method2",
                     G_CALLBACK(handleRequest), (void *)process);
    g_signal_connect(delegate, "handle-method3",
                     G_CALLBACK(handleRequest), nullptr);
}

void * getDelegate() {
    return aisdk_demo_skeleton_new();
}

gboolean onNewConnection(GDBusServer */* server */,
                         GDBusConnection *connection,
                         gpointer /* userData */) {
    GCredentials *credentials;
    gchar *s;

    credentials = g_dbus_connection_get_peer_credentials(connection);
    if (credentials == nullptr) {
        s = g_strdup("(no credentials received)");
    } else {
        s = g_credentials_to_string(credentials);
    }

    g_print("Client connected. Peer credentials: %s."
            " Negotiated capabilities: unix-fd-passing=%d\n",
            s,
            g_dbus_connection_get_capabilities(connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);

    auto *delegate = getDelegate();
    connectSignal(delegate);

    GError *error = nullptr;
    std::string objectPath = "/org/openkylin/aisdk/demo/object";
    auto isExported = g_dbus_interface_skeleton_export(
            G_DBUS_INTERFACE_SKELETON(delegate), connection, objectPath.c_str(),
            &error);
    if (!isExported) {
        g_printerr("Error creating server at address %s: %s\n",
                   objectPath.c_str(), error->message);
        g_error_free(error);
    }

    g_object_ref(connection);
    return true;
}

int main() {
    gchar opt_address[] = "unix:abstract=/org/openkylin/aisdk/demo/address";
    gchar *guid = g_dbus_generate_guid();
    GError *error = nullptr;

    auto server = g_dbus_server_new_sync(opt_address,        /* address */
                                     G_DBUS_SERVER_FLAGS_NONE, /* flags */
                                     guid,                     /* guid */
                                     nullptr, /* GDBusAuthObserver */
                                     nullptr, /* GCancellable */
                                     &error);
    if (server == nullptr) {
        g_printerr("Error creating server at address %s: %s\n",
                   opt_address, error->message);
        g_error_free(error);
        return -1;
    }

    g_dbus_server_start(server);

    g_free(guid);

    g_print("Server is listening at: %s\n", g_dbus_server_get_client_address(server));

    g_signal_connect(server, "new-connection",
                     G_CALLBACK(onNewConnection),
                     nullptr);

    /** create main loop, but do not start it. */
    GMainLoop *loop = g_main_loop_new(nullptr, FALSE);
    g_print("Server started.\n");

    g_main_loop_run(loop);

    g_object_unref(server);
    g_main_loop_unref(loop);
    return 0;
}